
package ctu.kfad.run.memory;

import ctu.kfad.run.ClassFileRepository;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class ClassSizeDescriptorRepository {

    private ClassFileRepository repository;

    public ClassSizeDescriptorRepository(ClassFileRepository repository) {
        this.repository = repository;
    }

    private Map<FieldClassHolder, MemoryFieldInfo> cacheFieldDescriptors = new HashMap<>();
    private Map<String, ClassSizeDescriptor> cacheClassDescriptors = new HashMap<>();
    private Map<String, int[]> cacheReferenceAddress = new HashMap<>();

    public MemoryFieldInfo getInfo(String fieldName, String className, String instanceClassName) {
        final FieldClassHolder key = new FieldClassHolder(fieldName, className, instanceClassName);
        final MemoryFieldInfo memoryFieldInfo = cacheFieldDescriptors.get(key);
        if (memoryFieldInfo != null) {
            return memoryFieldInfo;
        }
        ClassSizeDescriptor classDescriptor = getJavaClassSizeDescriptor(instanceClassName);
        final MemoryFieldInfo info = classDescriptor.getInfo(fieldName, className);
        cacheFieldDescriptors.put(key, info);
        return info;
    }

    public int getTotalSize(String className) {
        return getJavaClassSizeDescriptor(className).getTotalSize();
    }

    public int[] getAddressesForReferences(String className) {
        final int[] ints = cacheReferenceAddress.get(className);
        if (ints != null) {
            return ints;
        }
        final ClassSizeDescriptor javaClassSizeDescriptor = getJavaClassSizeDescriptor(className);
        final Set<Integer> referenceOffset = javaClassSizeDescriptor.getReferenceOffset();
        final int[] result = new int[referenceOffset.size()];
        final Iterator<Integer> iterator = referenceOffset.iterator();
        for (int i = 0; i < referenceOffset.size(); i++) {
            result[i] = iterator.next();
        }
        cacheReferenceAddress.put(className, result);
        return result;
    }


    private ClassSizeDescriptor getJavaClassSizeDescriptor(String className) {
        ClassSizeDescriptor classDescriptor = cacheClassDescriptors.get(className);
        if (classDescriptor == null) {
            classDescriptor = new ClassSizeDescriptor(className, repository);
            cacheClassDescriptors.put(className, classDescriptor);
        }
        return classDescriptor;
    }
}
